package org.tulkas.config;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;

/**
 * @author <a href="mailto:tulkas.web@gmail.com">Tulkas</a>
 *
 */
public class BasicConfig extends Configuration {
	public static final String DEFAULT_CONFIG_CHARSET = "UTF-8";

	private static final Pattern PTN_KEY_PART = Pattern.compile("[\\w-]+");

	private final Properties props = new Properties();
	private Configuration parent = null;

	public BasicConfig() {
	}

	public BasicConfig(Configuration parent) {
		this.parent = parent;
	}

	public static boolean isAvailableKeyPart(String keyPart) {
		return PTN_KEY_PART.matcher(keyPart).matches();
	}

	private void collectKeys(Set<String> set, Properties props, String prefix) {
		Enumeration<?> enu = props.propertyNames();
		while (enu.hasMoreElements()) {
			String key = (String) enu.nextElement();
			if (prefix == null || key.startsWith(prefix)) {
				set.add(key);
			}
		}
	}

	public String[] getOwnKeys(String prefix) {
		Set<String> ret = new TreeSet<String>();
		collectKeys(ret, props, prefix);

		String[] retA = new String[ret.size()];
		ret.toArray(retA);
		return retA;
	}

	public String[] getOwnKeys() {
		return getOwnKeys(null);
	}

	@Override
	public String[] getAllKeys(String prefix) {
		Set<String> ret = new TreeSet<String>();
		if (parent != null) {
			for (String key : parent.getAllKeys(prefix)) {
				ret.add(key);
			}
		}
		collectKeys(ret, props, prefix);

		String[] retA = new String[ret.size()];
		ret.toArray(retA);
		return retA;
	}

	/* (non-Javadoc)
	 * @see org.tulkas.config.Configuration#containsKey(java.lang.String)
	 */
	@Override
	public boolean containsKey(String key) {
		if (props.containsKey(key))
			return true;
		else if (parent != null && parent.containsKey(key))
			return true;
		else
			return false;
	}

	/* (non-Javadoc)
	 * @see org.tulkas.config.Configuration#getValue(java.lang.String)
	 */
	@Override
	public String getValue(String key) {
		String prop = props.getProperty(key);
		if (parent != null && (prop == null || "".equals(prop)))
			prop = parent.getValue(key);
		if (prop == null)
			return null;
		prop = prop.trim();
		if ("".equals(prop))
			return null;
		return prop;
	}

	/* (non-Javadoc)
	 * @see org.tulkas.config.Configuration#setValue(java.lang.String, java.lang.String)
	 */
	public void setValue(String key, String value) {
		props.setProperty(key, value);
	}

	public void load(Reader reader) throws IOException {
		props.load(reader);
	}

	public void save(Writer writer) throws IOException {
		props.store(writer, "");
	}

	public void load(InputStream is) throws IOException {
		props.load(new InputStreamReader(is, DEFAULT_CONFIG_CHARSET));
	}

	public void save(OutputStream os) throws IOException {
		props.store(new OutputStreamWriter(os, DEFAULT_CONFIG_CHARSET), "");
	}

	/* (non-Javadoc)
	 * @see org.tulkas.config.Configuration#remove(java.lang.String)
	 */
	public void remove(String key) {
		props.remove(key);
	}

	/* (non-Javadoc)
	 * @see org.tulkas.config.Configuration#clear()
	 */
	public void clear() {
		props.clear();
	}

	public void setValues(Properties values) {
		props.putAll(values);
	}

	public void setValues(BasicConfig config) {
		String[] keys = config.getAllKeys();
		for (String key : keys) {
			setValue(key, config.getValue(key));
		}
	}

	public Properties toProperties() {
		Properties ret = new Properties();
		if (parent != null) {
			String[] keys = parent.getAllKeys();
			for (String key : keys) {
				String value = parent.getValue(key);
				if (value != null) {
					ret.setProperty(key, value);
				}
			}
		}

		ret.putAll(props);
		return ret;
	}

	public BasicConfig getSubConfig(String prefix) {
		BasicConfig ret = new BasicConfig();
		String[] keys = getAllKeys(prefix);
		for (String key : keys) {
			ret.setValue(key.substring(prefix.length()), getValue(key));
		}
		return ret;
	}

	public Properties getSubProperties(String prefix) {
		Properties ret = new Properties();
		String[] keys = getAllKeys(prefix);
		for (String key : keys) {
			ret.setProperty(key.substring(prefix.length()), getValue(key));
		}
		return ret;
	}
}
